package br.org.acessobrasil.portal.util;

import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.struts2.StrutsStatics;

import br.org.acessobrasil.portal.action.Super;
import br.org.acessobrasil.portal.controle.SecurityManager;
import br.org.acessobrasil.portal.modelo.ArquivoLog;
import br.org.acessobrasil.portal.modelo.Privilegio;
import br.org.acessobrasil.portal.modelo.Sitio;
import br.org.acessobrasil.portal.modelo.Usuario;
import br.org.acessobrasil.portal.persistencia.LogDao;
import br.org.acessobrasil.portal.persistencia.PrivilegioDao;
import br.org.acessobrasil.portal.persistencia.PrivilegioDaoSpring;
import br.org.acessobrasil.portal.persistencia.UsuarioDaoSpring;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

/**
 * Controla se o usu�rio est� logado ou n�o verifica se ele pode ou n�o acessar
 * uma determinada action.
 * 
 * @author Fabio Issamu Oshiro
 * 
 */
public class LoginInterceptor extends AbstractInterceptor implements StrutsStatics {
	private static final Logger logger = Logger.getLogger(LoginInterceptor.class);
	private static final long serialVersionUID = 1125371586660775156L;
	public static final boolean PULA_LOGIN = false;
	private SecurityManager securityManager;
	public static final String USER_HANDLE = "BN_USER_SESSION";
	private static final String LOGIN_ATTEMPT = "BN_TENTATIVA_LOGIN";
	private static final String USERNAME = "BN_USUARIO";
	private static final String PASSWORD = "BN_SENHA";
	private static final String URL_SOLICITADA = "URL_SOLICITADA" ;
	private String mensagem;
	ArquivoLog logg = new ArquivoLog();
	private LogDao logDao = LogDao.getInstance();
	private PrivilegioDao privilegioDao = PrivilegioDaoSpring.getInstance();

	public void init() {
		logger.info("Intializing LoginInterceptor");
	}

	public void destroy() {
	}

	/**
	 * Intercepta todas as acoes do usuario
	 */
	public String intercept(ActionInvocation invocation) throws Exception {

		// Get the action context from the invocation so we can access the
		// HttpServletRequest and HttpSession objects.
		final ActionContext context = invocation.getInvocationContext();
		HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
		HttpServletResponse response = (HttpServletResponse) context.get(HTTP_RESPONSE);
		HttpSession session = request.getSession(true);

		// Tratamento do JSESSIONID, incompatibilicade com o webvox
		{
			if (session == null)
				return "login";
			response.addCookie(new Cookie("JSESSIONID", session.getId()));
		}

		String pagOrigem = request.getHeader("Referer");
		if (context.getName().equals("voltar")) {
			response.sendRedirect(session.getAttribute("PagOrigem").toString());
			return "";
		}
		session.setAttribute("PagOrigem", pagOrigem);

		String acao = getActionName(request);
		logger.debug("acao='"+acao+"'");
		if (acao.equals("instalar")) {
			// chama o instalador
			invocation.invokeActionOnly();
		}
		// Is there a "user" object stored in the user's HttpSession?
		Usuario usuario = (Usuario) session.getAttribute(USER_HANDLE);
		if (usuario == null) {
			// The user has not logged in yet.
			// O servi�o � de auto completar ou outro que n�o deva ir para o
			// login?
			// Nao mostra a tela de login no pedaco do ajax.
			if (acao.equals("autoCompletar") || acao.equals("verMensagemStatusEda")) {
				return ActionSupport.INPUT;
			}
			
			// Permite executar o cadastar Newsletter dentro de adm sem login
			if (acao.equals("cadastrarDestinatario"))
				invocation.invoke();
			// Permite executar o lembrar senha dentro de adm sem login
			if (acao.equals("lembrarSenhaGerenciador"))
				invocation.invoke();
			// Is the user attempting to log in right now?
			String loginAttempt = request.getParameter(LOGIN_ATTEMPT);
			if (loginAttempt != null && !loginAttempt.equals("")) {
				// The user is attempting to log in.
				// Process the user's login attempt.
				// e joga na sessao caso esteja com login e senha corretos
				if (processLoginAttempt(request, session)) {
					// The login succeeded send them the login-success page.
					usuario = (Usuario) session.getAttribute(USER_HANDLE);
					logDao.addLog(usuario, "Usu&aacute;rio logado com sucesso.");
					
					boolean loginDireto = false;
					
					if (usuario.getListSitio().size() == 0) {
						loginDireto = true;
					}else if (usuario.getListSitio().size() == 1) {
						Sitio sitio = ((Usuario) session.getAttribute(USER_HANDLE)).getListSitio().get(0);
						Privilegio privilegioAtual = privilegioDao.select(usuario, sitio);
						usuario.setPerfil(privilegioAtual.getPerfil());
						usuario.setSetor(privilegioAtual.getSetor());
						session.setAttribute(USER_HANDLE, usuario);
						session.setAttribute(Super.SITIO_LOGADO, sitio);
						loginDireto = true;
					}else if (usuario.getListSitio().size() > 0) {
						// ver se a url casa com algum sitio
						for (Sitio sitio : usuario.getListSitio()) {
							if(sitio.getUrl() != null){
								String[] urlsSitio = sitio.getUrl().split(",");
								for (int i = 0; i < urlsSitio.length; i++) {
									String urlAtual = urlsSitio[i];
									if (urlAtual != null && request.getRequestURL().toString().startsWith(urlAtual)) {
										Privilegio privilegioAtual = privilegioDao.select(usuario, sitio);
										usuario.setPerfil(privilegioAtual.getPerfil());
										usuario.setSetor(privilegioAtual.getSetor());
										session.setAttribute(USER_HANDLE, usuario);
										session.setAttribute(Super.SITIO_LOGADO, sitio);
										loginDireto = true;
										break;
									}
								}
							}
							if(loginDireto)
								break;
						}
					}
					
					if(loginDireto){
						//tenta seguir a url solicitada
						String referer = (String) request.getSession().getAttribute(URL_SOLICITADA);
						request.getSession().removeAttribute(URL_SOLICITADA);
						if(referer != null){
							String action = referer;
							if(action.contains(".action")){
								try{
									action = action.substring(0, action.indexOf(".action"));
									action = action.substring(action.lastIndexOf("/") + 1);
									if(!action.equals("logout") && securityManager.podeExecutarAcao(usuario, action)){
										response.sendRedirect(referer);	
									}
								}catch (Exception e) {}
							}
						}
						return "login-direct";
					}else{
						//TODO Implementar o seguir apos a tela de escolha do sitio
						logger.debug("Usuario logado, escolher sitio. Total de sites = " + usuario.getListSitio().size());
						return "login-success";
					}
				} else {
					// The login failed. Set an error if we can on the action.
					Object action = invocation.getAction();
					if (action instanceof com.opensymphony.xwork2.ValidationAware) {
						((com.opensymphony.xwork2.ValidationAware) action).addActionError(mensagem);
						usuario = null;
						logDao.addLog(usuario, mensagem + "[ " + request.getParameter(USERNAME) + " ] ");
					}
				}
			}

			//salva a url solicitada para seguir apos sucesso no login
			try{
				//Ignora se nao for get
				if(request.getMethod().toUpperCase().equals("GET")){
					String url = request.getRequestURL().toString();
					if(url.contains(".action")){
						String qs = request.getQueryString();
						if(qs != null){
							url += "?" + qs;
						}
						request.getSession().setAttribute(URL_SOLICITADA, url);
					}
				}
			} catch (Exception e) {}

			// Either the login attempt failed or the user hasn't tried to login
			// yet,
			// and we need to send the login form.
			return "login";
		} else {
			if (acao.equals("logout")) {
				session.removeAttribute(USER_HANDLE);
			}
			if (this.securityManager.podeExecutarAcao(usuario, acao)) {
				return invocation.invoke();
			} else {
				// Ele n�o pode executar a acao
				return "ilegal-action";
			}
		}
	}

	/**
	 * Retorna a acao de acordo com a url
	 * 
	 * @param request
	 * @return
	 */
	private String getActionName(HttpServletRequest request) {
		String a = request.getRequestURI();
		int ini = a.lastIndexOf("/");
		if (ini == -1)
			return "";
		int fim = a.indexOf(".action", ini);
		ini++;
		if (fim == -1 || ini > fim)
			return "";
		return a.substring(ini, fim);
	}

	/**
	 * Attempt to process the user's login attempt delegating the work to the
	 * SecurityManager.
	 */
	public boolean processLoginAttempt(HttpServletRequest request, HttpSession session) {
		// Get the username and password submitted by the user from the
		// HttpRequest.
		String username = request.getParameter(USERNAME);
		String password = request.getParameter(PASSWORD);

		// Use the security manager to validate the user's username and
		// password.
		Usuario user = securityManager.login(username, password);
		if (user == null) {
			// The user did not successfully log in. Return false.
			Usuario usuario = UsuarioDaoSpring.getInstance().procurarPorLogin(username);
			if(usuario != null){
				long tentativa = usuario.getNuTentativa()==null ? 0 : usuario.getNuTentativa();
				if (tentativa<=5){
					logger.debug("Tentativa de login n&uacute;mero: "+tentativa);
					usuario.setNuTentativa(tentativa+1);
					UsuarioDaoSpring.getInstance().atualizar(usuario);
					if(tentativa==2){
						mensagem = "Sr(a). Usu&aacute;rio(a), caso tenha esquecido sua senha, entre na &aacute;rea: \"Esqueci minha senha\".";
					}else if(tentativa==5){
						mensagem = "Aten&ccedil;&atilde;o: caso haja um erro de senha novamente, o acesso a conta ser&aacute; bloqueado.";
					}else{
						mensagem = "Usu&aacute;rio ou senha incorreta.";
					}
					return false;
				}else{
					logger.debug("Numero de tentativas maxima ultrapassada. ("+tentativa+")");
					usuario.setDtEspera(new Date(new Date().getTime()+15*60*1000));
					UsuarioDaoSpring.getInstance().atualizar(usuario);
					mensagem = "Aten&ccedil;&atilde;o: Por medida de seguran&ccedil;a, seu acesso foi bloqueado por alguns minutos devido ao n&uacute;mero de tentativas erradas. Tente novamente mais tarde.";
					return false;
				}
			}
			mensagem = "Usu&aacute;rio ou senha n&atilde;o existe.";
			return false;
		} else if (!user.isCoAtivo()) {
			mensagem = "O seu cadastro foi desativado.";
			return false;
		} else {
			// The user has successfully logged in. Store their user object in
			// their HttpSession. Then return true.
			user.setNuTentativa(0L);
			user.setDtEspera(null);
			UsuarioDaoSpring.getInstance().atualizar(user);
			session.setAttribute(USER_HANDLE, user);
			return true;
		}
	}

	public void setSecurityManager(SecurityManager in) {
		// log.debug("Setting security manager using: " + in.toString());
		this.securityManager = in;
	}

}